﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WebLayer;
using System.IO;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Runtime.Serialization;
using TableParsers;

using SpeakupBackend;
using SpeakupBackend.Themes;
using SpeakupBackend.Classes;
using System.ComponentModel;

using System.Collections.Observable.ExtendedEventArgs;

namespace Frontend
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    partial class MainWindow : Window, IStateSerializable 
    {
        static string BackendContextFile = "backend.bin";

        ClassPanelList ClassPanelList = new ClassPanelList();
        
        Refresher Refresher = new Refresher();
        BackendContext BackendCtx = new BackendContext();

        public static LoginToken LoginToken
        {
            get
            {
                MainWindow window = Application.Current.MainWindow as MainWindow;
                if (window == null) return null;

                if (window.LoginPanel.User == string.Empty ||
                    window.LoginPanel.Password == string.Empty)
                {
                    return null;
                }

                return new LoginToken(window.LoginPanel.User, 
                                      window.LoginPanel.Password, 
                                      ((int)window.LoginPanel.School).ToString());
            }
        }

        #region Initialization/Deinitialization
        
        public MainWindow()
        {
            InitializeComponent();

            ClassPanelList.CollectionChanged += ClassPanelList_CollectionChanged;
            ClassPanelList.SelectionChanged += ClassPanelList_SelectionChanged;
            ClassPanelList.HighlightChanged += ClassPanelList_HighlightChanged;

            /*restore backend */
            LoadBackend();

            /*signup for events in backend, controls and other sources*/
            SignupForEvents();

            TodoControl.BackendCtx = BackendCtx;

            ThemesControl.BackendCtx = BackendCtx;
            ThemesControl.CurrentWeek = Week.GetCurrentWeek();
            ThemesControl.RefreshGrids();

            CurrentUnitTextBlock.Text = BackendCtx.Stats.Unit.ToString();
            CurrentLevelTextBlock.Text = BackendCtx.Stats.Level.ToString();
            CurrentWeekTextBlock.Text = Week.GetCurrentWeek().ToString();

            LoadUIState();
        }

        private void SignupForEvents()
        {
            {
                var proxy = new DelegateProxy<NotifyCollectionChangedEventHandler>();
                BackendCtx.Classes.CollectionChanged += proxy.ProxyConsumer;
                proxy.ProxySource += SpeakupClassList_CollectionChanged;
            }

            {
                var proxy = new DelegateProxy<EventHandler<PropertyChangedEventArgs>>();
                BackendCtx.Classes.PropertyChanged += proxy.ProxyConsumer;
                proxy.ProxySource += SpeakupClassList_ClassPropertyChanged;
            }

            {
                var proxy = new DelegateProxy<EventHandler<EventArgs>>();
                BackendCtx.Stats.PropertyChanged += proxy.ProxyConsumer;
                proxy.ProxySource += SpeakupPersonalStats_Refreshed;
            }

            {
                var proxy = new DelegateProxy<NotifyCollectionChangedEventHandler>();
                BackendCtx.WorkshopThemes.CollectionChanged += proxy.ProxyConsumer;
                proxy.ProxySource += (s, e) => ThemesControl.RefreshGrids();
            }

            ClassTypeFilterPanel.Changed += ClassTypeFilterPanel_Changed;
            DateTimeFilterPanel.Changed += DateTimeFilterPanel_Changed;

            Refresher.StateChanged += Refresher_StateChanged;
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            SaveUIState();
            SaveBackend();
        }

        #endregion

        #region Class list syncronization (backend/frontend)

        /// <summary>
        /// Sync panel list with existing class list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void SpeakupClassList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (Class speakupClass in e.NewItems)
                {
                    ClassPanelList.Add(new ClassPanel(speakupClass));                    
                }                
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (Class speakupClass in e.OldItems)
                {
                    foreach (ClassPanel panel in ClassPanelList)
                    {
                        if (panel.SpeakupClass == speakupClass)
                        {
                            ClassPanelList.Remove(panel);
                            break;
                        }
                    }
                }
                
            }
            else if(e.Action == NotifyCollectionChangedAction.Reset)
            {
                ClassPanelList.Clear();                
            }

            ClassCount.Text = BackendCtx.Classes.Count.ToString();
        }

        /// <summary>
        /// Sync time sheet and panel list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ClassPanelList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (ClassPanel classPanel in e.NewItems)
                {
                    TimeSlot timeSlot = new TimeSlot(classPanel.SpeakupClass.Time.Begin,
                                                 classPanel.SpeakupClass.Time.End);

                    TimeSheet.SetTimeSlot(classPanel, timeSlot);
                    TimeSheet.SetDaySlot(classPanel, classPanel.SpeakupClass.Time.Date);

                    ClassTimeSheet.AddChildren(classPanel);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (ClassPanel classPanel in e.OldItems)
                {
                    ClassTimeSheet.RemoveChildren(classPanel);
                    
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                foreach (var child in ClassTimeSheet.Children.ToList())
                {
                    ClassTimeSheet.RemoveChildren(child);
                }
            }
        }

        #endregion

        #region Class highlight / selection management
        
        void ClassPanelList_HighlightChanged(object sender, ClassPanelList.ClassChangedEventArgs e)
        {
            if (e.SpeakupClass == null)
            {
                Class speakupClass = null;
                if(ClassPanelList.SelectedItem != null) 
                {
                    speakupClass = ClassPanelList.SelectedItem.SpeakupClass;
                }

                ClassInfoPanel.Update(speakupClass);
            }
            else
            {
                ClassInfoPanel.Update(e.SpeakupClass);
            }
        }

        void ClassPanelList_SelectionChanged(object sender, ClassPanelList.ClassChangedEventArgs e)
        {
            ClassInfoPanel.Update(e.SpeakupClass);            
        }

        void SpeakupClassList_ClassPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            
        }

        #endregion

        #region backend refresh functionality
        
        private void LoginPanel_Refresh(object sender, LoginPanelRefreshEventArgs e)
        {

            if (Refresher.State == RefresherState.InPorgress)
            {
                Trace.TraceWarning("Refresh requested while refresh operation is in progress");
                return;
            }

            RefreshStatusPanel.Visibility = Visibility.Visible;
            RefreshCancelButton.IsEnabled = true;

            LoginPanel.IsRefreshing = true;

            BackendCtx.LoginToken = new LoginToken(LoginPanel.User, 
                                                   LoginPanel.Password, 
                                                   ((int)LoginPanel.School).ToString());
            Refresher.StartRefresh(BackendCtx);
        }

        void Refresher_StateChanged(object sender, RefresherStateEventArgs e)
        {
            LoginPanel.IsRefreshing = (e.Refresher.State == RefresherState.InPorgress);

            if (e.Refresher.State != RefresherState.InPorgress)
            {
                RefreshStatusPanel.Visibility = Visibility.Collapsed;
            }
            
            if (e.Refresher.State == RefresherState.Completed)
            {
                SaveBackend();
                ThemesControl.RefreshGrids();
            }
            else if (e.Refresher.State == RefresherState.ErrorOccured)
            {
                MessageBox.Show("Error occured during refresh:\n" + e.Refresher.ErrorMessage,
                                    "Refresh error", MessageBoxButton.OK, MessageBoxImage.Error);                
            }
            else if (e.Refresher.State == RefresherState.Cancelled)
            {
                MessageBox.Show("User cancelled refresh.", "Cancelled", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }            
        }

        void SpeakupPersonalStats_Refreshed(object sender, EventArgs e)
        {
            ThemesControl.CurrentLevel = Level.GetLevel(BackendCtx.Stats.Level);

            CurrentUnitTextBlock.Text = BackendCtx.Stats.Unit.ToString();
            CurrentLevelTextBlock.Text = BackendCtx.Stats.Level.ToString();
        }
        
        private void RefreshCancel_Click(object sender, RoutedEventArgs e)
        {
            Refresher.CancelRefresh();
            RefreshCancelButton.IsEnabled = false;
        }

        #endregion

        #region Type Filter and DateTime filter functionality
        
        void ClassTypeFilterPanel_Changed(object sender, EventArgs e)
        {
            ClassPanelList.UseManualMask = ClassTypeFilterPanel.UseManualFilter;

            ClassPanelMask mask = new ClassPanelMask();
            mask[ClassType.Club] = ClassTypeFilterPanel.HighlightClub;
            mask[ClassType.Conversation] = ClassTypeFilterPanel.HighlightConversation;
            mask[ClassType.Grammar] = ClassTypeFilterPanel.HighlightGrammar;
            mask[ClassType.Group] = ClassTypeFilterPanel.HighlightGroup;
            mask[ClassType.Reading] = ClassTypeFilterPanel.HighlightReading;
            mask[ClassType.Writing] = ClassTypeFilterPanel.HighlightWriting;

            ClassPanelList.ManualMask = mask;
            ClassPanelList.DefaultEnabledStatus = ClassTypeFilterPanel.HighlightOther;            
        }

        void DateTimeFilterPanel_Changed(object sender, DateAndTimeFilterEventArgs e)
        {
            if (e.IsFilterEnabled)
            {
                ClassTimeSheet.UpdateDate(e.DateBegin, e.DateEnd);
                ClassTimeSheet.UpdateTime(e.TimeBegin, e.TimeEnd);                
            }
            else
            {
                ClassTimeSheet.UpdateDate(DateTime.Today,
                                          DateTime.Today + TimeSpan.FromDays(13));
                ClassTimeSheet.UpdateTime(TimeSpan.FromHours(8), TimeSpan.FromHours(22));
            }
        }

        private void ClearClasses_Click(object sender, RoutedEventArgs e)
        {
            BackendCtx.Classes.Clear();

            foreach (var theme in BackendCtx.GroupThemes)
            {
                theme.ClearClasses();
            }

            foreach (var theme in BackendCtx.ClubThemes)
            {
                theme.ClearClasses();
            }

            foreach (var theme in BackendCtx.WorkshopThemes)
            {
                theme.ClearClasses();
            }

            ThemesControl.RefreshGrids();
        }

        #endregion

        #region Backend persistency

        private void SaveBackend()
        {
            try
            {
                BackendCtx.Save(BackendContextFile);
            }
            catch (Exception)
            {
                //just ignore the error.
                if (File.Exists(BackendContextFile))
                {
                    File.Delete(BackendContextFile);
                }
            }
        }

        private void LoadBackend()
        {
            try
            {
                BackendCtx = BackendContext.Load(BackendContextFile);

                foreach (Class cl in BackendCtx.Classes)
                {
                    ClassPanelList.Add(new ClassPanel(cl));
                }
            }
            catch (Exception)
            {
                if (File.Exists(BackendContextFile))
                {
                    File.Delete(BackendContextFile);
                }
            }
        }

        #endregion

        #region UI state persistency
        
        static readonly string UIStateFile = "UIState.bin";
        static readonly string ClassTypeFilterPanelStateKey = typeof(ClassTypeFilterPanel).Name;
        static readonly string DateTimeFilterPanelStateKey = typeof(DateTimeFilterPanel).Name;
        static readonly string LegendPanelStateKey = typeof(LegendPanel).Name;
        static readonly string LoginPanelStateKey = typeof(LoginPanel).Name;

        

        private void SaveUIState()
        {
            try
            {
                StateSerializer.SaveState(this, UIStateFile);
            }
            catch (Exception)
            {
                //just ignore the error.
                if (File.Exists(UIStateFile))
                {
                    File.Delete(UIStateFile);
                }
            }
        }

        private void LoadUIState()
        {
            try
            {
                if (File.Exists(UIStateFile))
                {
                    StateSerializer.LoadState(this, UIStateFile);
                }
            }
            catch (Exception)
            {
                if (File.Exists(UIStateFile))
                {
                    File.Delete(UIStateFile);
                }
            }
        }

        public StateDictionary GetState()
        {
            StateDictionary state = new StateDictionary();
            state.AddValue(ClassTypeFilterPanelStateKey, ClassTypeFilterPanel.GetState());
            state.AddValue(DateTimeFilterPanelStateKey, DateTimeFilterPanel.GetState());
            state.AddValue(LegendPanelStateKey, LegendPanel.GetState());
            state.AddValue(LoginPanelStateKey, LoginPanel.GetState());            
            
            return state;
        }

        public void SetState(StateDictionary state)
        {
            ClassTypeFilterPanel.SetState(state.GetState(ClassTypeFilterPanelStateKey));
            DateTimeFilterPanel.SetState(state.GetState(DateTimeFilterPanelStateKey));
            LegendPanel.SetState(state.GetState(LegendPanelStateKey));
            LoginPanel.SetState(state.GetState(LoginPanelStateKey));
        }

        #endregion
    }
}
